home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Tools (InfoMagic)
/
Internet Tools.iso
/
archival
/
mirror
/
experimental
/
ftp.pl.Z
/
ftp.pl
Wrap
Perl Script
|
1994-11-24
|
25KB
|
1,272 lines
#-*-perl-*-
# This is a wrapper to the lchat.pl routines that make life easier
# to do ftp type work.
# Mostly by Lee McLoughlin <lmjm@doc.ic.ac.uk>
# based on original version by Alan R. Martello <al@ee.pitt.edu>
# And by A.Macpherson@bnr.co.uk for multi-homed hosts
#
# Basic usage:
# $ftp_port = 21;
# $retry_call = 1;
# $attempts = 2;
# if( &ftp'open( $site, $ftp_port, $retry_call, $attempts ) != 1 ){
# die "failed to open ftp connection";
# }
# if( ! &ftp'login( $user, $pass ) ){
# die "failed to login";
# }
# &ftp'type( $text_mode ? 'A' : 'I' );
# if( ! &ftp'get( $remote_filename, $local_filename, 0 ) ){
# die "failed to get file;
# }
# &ftp'quit();
#
#
# $Id: ftp.pl,v 2.6 1994/06/06 18:37:37 lmjm Exp lmjm $
# $Log: ftp.pl,v $
# Revision 2.6 1994/06/06 18:37:37 lmjm
# Switched to lchat - a subset of chat.
# Allow for 'remote help's need to parse the help strings in the continuations
# Use real_site for proxy connections.
# Allow for cr stripping and corrected use of buffer (from Andrew).
#
# Revision 2.5 1994/04/29 20:11:04 lmjm
# Converted to use rfc1123.
#
# Revision 2.4 1994/01/26 14:59:07 lmjm
# Added DG result code.
#
# Revision 2.3 1994/01/18 21:58:18 lmjm
# Reduce calls to sigset.
# Reset to old signal after use.
#
# Revision 2.2 1993/12/14 11:09:06 lmjm
# Use installed socket.ph.
# Allow for more returns.
#
# Revision 2.1 1993/06/28 15:02:00 lmjm
# Full 2.1 release
#
#
require 'sys/socket.ph';
# lchat.pl is a special subset of chat2.pl that avoids some memory leaks.
require 'lchat.pl';
package ftp;
$retry_pause = 60; # Pause before retrying a login.
if( defined( &main'PF_INET ) ){
$pf_inet = &main'PF_INET;
$sock_stream = &main'SOCK_STREAM;
local($name, $aliases, $proto) = getprotobyname( 'tcp' );
$tcp_proto = $proto;
}
else {
# XXX hardwired $PF_INET, $SOCK_STREAM, 'tcp'
# but who the heck would change these anyway? (:-)
$pf_inet = 2;
$sock_stream = 1;
$tcp_proto = 6;
}
# If the remote ftp daemon doesn't respond within this time presume its dead
# or something.
$timeout = 120;
# Timeout a read if I don't get data back within this many seconds
$timeout_read = 3 * $timeout;
# Timeout an open
$timeout_open = $timeout;
$ftp'version = '$Revision: 2.6 $';
# This is a "global" it contains the last response from the remote ftp server
# for use in error messages
$ftp'response = "";
# Also ftp'NS is the socket containing the data coming in from the remote ls
# command.
# The size of block to be read or written when talking to the remote
# ftp server
$ftp'ftpbufsize = 4096;
# How often to print a hash out, when debugging
$ftp'hashevery = 1024;
# Output a newline after this many hashes to prevent outputing very long lines
$ftp'hashnl = 70;
# Is there a connection open?
$ftp'service_open = 0;
# If a proxy connection then who am I really talking to?
$real_site = "";
# Where error/log reports are sent to
$ftp'showfd = 'STDERR';
# Should a 421 be treated as a connection close and return 99 from
# ftp'expect. This is against rfc1123 recommendations but I've found
# it to be a wise default.
$drop_on_421 = 1;
# Name of a function to call on a pathname to map it into a remote
# pathname.
$ftp'mapunixout = '';
$ftp'manunixin = '';
# This is just a tracing aid.
$ftp_show = 0;
# Wether to keep the continuation messages so the user can look at them
$ftp'keep_continuations = 0;
# Uncomment to turn on lots of debugging.
# &ftp'debug( 10 );
sub ftp'debug
{
$ftp_show = @_[0];
if( $ftp_show > 9 ){
$chat'debug = 1;
}
}
sub ftp'set_timeout
{
local( $to ) = @_;
return if $to == $timeout;
$timeout = $to;
$timeout_open = $timeout;
$timeout_read = 3 * $timeout;
if( $ftp_show ){
print $ftp'showfd "ftp timeout set to $timeout\n";
}
}
sub ftp'open_alarm
{
die "timeout: open";
}
sub ftp'timed_open
{
local( $site, $ftp_port, $retry_call, $attempts ) = @_;
local( $connect_site, $connect_port );
local( $ret );
alarm( $timeout_open );
while( $attempts-- ){
if( $ftp_show ){
print $ftp'showfd "proxy connecting via $proxy_gateway [$proxy_ftp_port]\n" if $proxy;
print $ftp'showfd "Connecting to $site";
if( $ftp_port != 21 ){
print $ftp'showfd " [port $ftp_port]";
}
print $ftp'showfd "\n";
}
if( $proxy ) {
if( ! $proxy_gateway ) {
# if not otherwise set
$proxy_gateway = "internet-gateway";
}
if( $debug ) {
print $ftp'showfd "using proxy services of $proxy_gateway, ";
print $ftp'showfd "at $proxy_ftp_port\n";
}
$connect_site = $proxy_gateway;
$connect_port = $proxy_ftp_port;
$real_site = $site;
}
else {
$connect_site = $site;
$connect_port = $ftp_port;
}
if( ! &chat'open_port( $connect_site, $connect_port ) ){
if( $retry_call ){
print $ftp'showfd "Failed to connect\n" if $ftp_show;
next;
}
else {
print $ftp'showfd "proxy connection failed " if $proxy;
print $ftp'showfd "Cannot open ftp to $connect_site\n" if $ftp_show;
return 0;
}
}
$ret = &ftp'expect( $timeout,
2, 1 ); # ready for login to $site
if( $ret != 1 ){
&chat'close();
next;
}
return 1;
}
continue {
print $ftp'showfd "Pausing between retries\n";
sleep( $retry_pause );
}
return 0;
}
sub main'ftp__sighandler
{
local( $sig ) = @_;
local( $msg ) = "Caught a SIG$sig flagging connection down";
$ftp'service_open = 0;
if( $ftp_logger ){
eval "&$ftp_logger( \$msg )";
}
}
sub ftp'set_signals
{
$ftp_logger = @_;
$SIG{ 'PIPE' } = "ftp__sighandler";
}
# Set the mapunixout and mapunixin functions
sub ftp'set_namemap
{
($ftp'mapunixout, $ftp'mapunixin) = @_;
if( $debug ) {
print $ftp'showfd "mapunixout = $ftp'mapunixout, $mapunixin = $ftp'mapunixin\n";
}
}
sub ftp'open
{
local( $site, $ftp_port, $retry_call, $attempts ) = @_;
local( $old_sig ) = $SIG{ 'ALRM' };
$SIG{ 'ALRM' } = "ftp\'open_alarm";
local( $ret ) = eval "&timed_open( '$site', $ftp_port, $retry_call, $attempts )";
alarm( 0 );
$SIG{ 'ALRM' } = $old_sig;
if( $@ =~ /^timeout/ ){
return -1;
}
if( $ret ){
$ftp'service_open = 1;
}
return $ret;
}
sub ftp'login
{
local( $remote_user, $remote_password ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $proxy ){
# Should site or real_site be used here?
&ftp'send( "USER $remote_user@$real_site" );
}
else {
&ftp'send( "USER $remote_user" );
}
$ret = &ftp'expect( $timeout,
2, 1, # $remote_user logged in
331, 2, # send password for $remote_user
332, 0 ); # account for login - not yet supported
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( $ret == 1 ){
# Logged in no password needed
return 1;
}
elsif( $ret == 2 ){
# A password is needed
&ftp'send( "PASS $remote_password" );
$ret = &ftp'expect( $timeout,
2, 1 ); # $remote_user logged in
if( $ret == 99 ){
&service_closed();
}
elsif( $ret == 1 ){
# Logged in
return 1;
}
}
# If I got here I failed to login
return 0;
}
sub service_closed
{
$ftp'service_open = 0;
&chat'close();
}
sub ftp'close
{
&ftp'quit();
$ftp'service_open = 0;
&chat'close();
}
# Change directory
# return 1 if successful
# 0 on a failure
sub ftp'cwd
{
local( $dir ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $ftp'mapunixout ){
$dir = eval "&$ftp'mapunixout( \$dir, 'd' )";
}
&ftp'send( "CWD $dir" );
$ret = &ftp'expect( $timeout,
2, 1 ); # working directory = $dir
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
# Get a full directory listing:
# &ftp'dir( remote LIST options )
# Start a list going with the given options.
# Presuming that the remote deamon uses the ls command to generate the
# data to send back then then you can send it some extra options (eg: -lRa)
# return 1 if sucessful and 0 on a failure
sub ftp'dir_open
{
local( $options ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( ! &ftp'open_data_socket() ){
return 0;
}
if( $options ){
&ftp'send( "LIST $options" );
}
else {
&ftp'send( "LIST" );
}
$ret = &ftp'expect( $timeout,
1, 1 ); # reading directory
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( ! $ret ){
&ftp'close_data_socket;
return 0;
}
accept( NS, S ) || die "accept failed $!";
#
# the data should be coming at us now
#
return 1;
}
# Close down reading the result of a remote ls command
# return 1 if successful and 0 on failure
sub ftp'dir_close
{
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
# read the close
#
$ret = &ftp'expect($timeout,
2, 1 ); # transfer complete, closing connection
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
# shut down our end of the socket
&ftp'close_data_socket;
if( ! $ret ){
return 0;
}
return 1;
}
# Quit from the remote ftp server
# return 1 if successful and 0 on failure
sub ftp'quit
{
local( $ret );
$site_command_check = 0;
@site_command_list = ();
if( ! $ftp'service_open ){
return 0;
}
&ftp'send( "QUIT" );
$ret = &ftp'expect( $timeout,
2, 1 ); # transfer complete, closing connection
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
# Support for ftp'read
sub ftp'read_alarm
{
die "timeout: read";
}
# Support for ftp'read
sub ftp'timed_read
{
alarm( $timeout_read );
return sysread( NS, $ftpbuf, $ftpbufsize );
}
# Do not use this routing use ftp'get
sub ftp'read_nosel
{
if( ! $ftp'service_open ){
return -1;
}
local( $ret ) = eval '&timed_read()';
alarm( 0 );
if( $@ =~ /^timeout/ ){
return -1;
}
return $ret;
}
sub ftp'read
{
if( ! $ftp'service_open ){
return -1;
}
$nfound = select( $read_out = $read_in, undef, undef, $to = $timeout_read );
if( $nfound <= 0 ){
return -1;
}
return sysread( NS, $ftpbuf, $ftpbufsize );
}
sub ftp'dostrip
{
($strip_cr ) = @_;
}
# Get a remote file back into a local file.
# If no loc_fname passed then uses rem_fname.
# returns 1 on success and 0 on failure
sub ftp'get
{
local($rem_fname, $loc_fname, $restart ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $loc_fname eq "" ){
$loc_fname = $rem_fname;
}
if( ! &ftp'open_data_socket() ){
print $ftp'showfd "Cannot open data socket\n";
return 0;
}
if( $loc_fname ne '-' ){
# Find the size of the target file
local( $restart_at ) = &ftp'filesize( $loc_fname );
if( $restart && $restart_at > 0 && &ftp'restart( $restart_at ) ){
$restart = 1;
# Make sure the file can be updated
chmod( 0644, $loc_fname );
}
else {
$restart = 0;
unlink( $loc_fname );
}
}
if( $ftp'mapunixout ){
$rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
}
&ftp'send( "RETR $rem_fname" );
$ret = &ftp'expect( $timeout,
1, 1 ); # receiving $rem_fname
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( $ret != 1 ){
print $ftp'showfd "Failure on 'RETR $rem_fname' command\n";
# shut down our end of the socket
&ftp'close_data_socket;
return 0;
}
accept( NS, S ) || die "accept failed $!";
#
# the data should be coming at us now
#
#
# open the local fname
# concatenate on the end if restarting, else just overwrite
if( !open( FH, ($restart ? '>>' : '>') . $loc_fname ) ){
print $ftp'showfd "Cannot create local file $loc_fname\n";
# shut down our end of the socket
&ftp'close_data_socket;
return 0;
}
local( $start_time ) = time;
local( $bytes, $lasthash, $hashes ) = (0, 0, 0);
# Use these three lines if you do not have the select() SYSTEM CALL in
# your perl. There appears to be a memory leak in using these
# and they are usually slower - so only use if you have to!
# Also comment back in the $SIG... line at the end of the while() loop.
# local( $old_sig ) = $SIG{ 'ALRM' };
# $SIG{ 'ALRM' } = "ftp\'read_alarm";
# while( ($len = &ftp'read_nosel()) > 0 ){
# If you have select() then use the following two lines.
vec( $read_in, fileno( NS ), 1 ) = 1;
while( ($len = &ftp'read()) > 0 ){
$bytes += $len;
if( $strip_cr ){
$ftp'ftpbuf =~ s/\r//g;
}
if( $ftp_show ){
while( $bytes > ($lasthash + $ftp'hashevery) ){
print $ftp'showfd '#';
$lasthash += $ftp'hashevery;
$hashes++;
if( ($hashes % $ftp'hashnl) == 0 ){
print $ftp'showfd "\n";
}
}
}
if( ! print FH $ftp'ftpbuf ){
print $ftp'showfd "\nfailed to write data";
$bytes = -1;
last;
}
}
# Add the next line back if you don't have select().
# $SIG{ 'ALRM' } = $old_sig;
close( FH );
# shut down our end of the socket
&ftp'close_data_socket;
if( $len < 0 ){
print $ftp'showfd "\ntimed out reading data!\n";
return 0;
}
if( $ftp_show && $bytes > 0 ){
if( $hashes && ($hashes % $ftp'hashnl) != 0 ){
print $ftp'showfd "\n";
}
local( $secs ) = (time - $start_time);
if( $secs <= 0 ){
$secs = 1; # To avoid a divide by zero;
}
local( $rate ) = int( $bytes / $secs );
print $ftp'showfd "Got $bytes bytes ($rate bytes/sec)\n";
}
#
# read the close
#
$ret = &ftp'expect( $timeout,
2, 1 ); # transfer complete, closing connection
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( $ret && $bytes < 0 ){
$ret = 0;
}
return $ret;
}
sub ftp'delete
{
local( $rem_fname ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $ftp'mapunixout ){
$rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
}
&ftp'send( "DELE $rem_fname" );
$ret = &ftp'expect( $timeout,
2, 1 ); # Deleted $rem_fname
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret == 1;
}
sub ftp'deldir
{
local( $fname ) = @_;
# not yet implemented
# RMD
}
# UPDATE ME!!!!!!
# Add in the hash printing and newline conversion
sub ftp'put
{
local( $loc_fname, $rem_fname ) = @_;
local( $strip_cr );
if( ! $ftp'service_open ){
return 0;
}
if( $loc_fname eq "" ){
$loc_fname = $rem_fname;
}
if( ! &ftp'open_data_socket() ){
return 0;
}
if( $ftp'mapunixout ){
$rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
}
&ftp'send( "STOR $rem_fname" );
#
# the data should be coming at us now
#
local( $ret ) =
&ftp'expect( $timeout,
1, 1 ); # sending $loc_fname
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( $ret != 1 ){
# shut down our end of the socket
&ftp'close_data_socket;
return 0;
}
accept( NS, S ) || die "accept failed $!";
#
# the data should be coming at us now
#
#
# open the local fname
#
if( !open( FH, "<$loc_fname" ) ){
print $ftp'showfd "Cannot open local file $loc_fname\n";
# shut down our end of the socket
&ftp'close_data_socket;
return 0;
}
while( <FH> ){
if( ! $ftp'service_open ){
last;
}
print NS ;
}
close( FH );
# shut down our end of the socket to signal EOF
&ftp'close_data_socket;
#
# read the close
#
$ret = &ftp'expect( $timeout,
2, 1 ); # transfer complete, closing connection
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( ! $ret ){
print $ftp'showfd "Failure on 'STOR $loc_fname' command\n";
}
return $ret;
}
sub ftp'restart
{
local( $restart_point, $ret ) = @_;
if( ! $ftp'service_open ){
return 0;
}
&ftp'send( "REST $restart_point" );
#
# see what they say
$ret = &ftp'expect( $timeout,
3, 1 ); # restarting at $restart_point
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
# Set the file transfer type
sub ftp'type
{
local( $type ) = @_;
if( ! $ftp'service_open ){
return 0;
}
&ftp'send( "TYPE $type" );
#
# see what they say
$ret = &ftp'expect( $timeout,
2, 1 ); # file type set to $type
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
$site_command_check = 0;
@site_command_list = ();
# routine to query the remote server for 'SITE' commands supported
sub ftp'site_commands
{
local( $ret );
@site_command_list = ();
$site_command_check = 0;
if( ! $ftp'service_open ){
return @site_command_list;
}
# if we havent sent a 'HELP SITE', send it now
if( !$site_command_check ){
$site_command_check = 1;
&ftp'send( "HELP SITE" );
# assume the line in the HELP SITE response with the 'HELP'
# command is the one for us
$ftp'keep_continuations = 1;
$ret = &ftp'expect( $timeout,
".*HELP.*", 1 );
$ftp'keep_continuations = 0;
if( $ret == 99 ){
&service_closed();
return @site_command_list;
}
if( $ret != 0 ){
print $ftp'showfd "No response from HELP SITE ($ret)\n" if( $ftp_show );
}
@site_command_list = split(/\s+/, $ftp'response);
}
return @site_command_list;
}
# return the pwd, or null if we can't get the pwd
sub ftp'pwd
{
local( $ret, $cwd );
if( ! $ftp'service_open ){
return 0;
}
&ftp'send( "PWD" );
#
# see what they say
$ret = &ftp'expect( $timeout,
2, 1 ); # working dir is
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
if( $ret ){
if( $ftp'response =~ /^2\d\d\s*"(.*)"\s.*$/ ){
$cwd = $1;
}
}
return $cwd;
}
# return 1 for success, 0 for failure
sub ftp'mkdir
{
local( $path ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $ftp'mapunixout ){
$path = eval "&$ftp'mapunixout( \$path, 'f' )";
}
&ftp'send( "MKD $path" );
#
# see what they say
$ret = &ftp'expect( $timeout,
2, 1 ); # made directory $path
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
# return 1 for success, 0 for failure
sub ftp'chmod
{
local( $path, $mode ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $ftp'mapunixout ){
$path = eval "&$ftp'mapunixout( \$path, 'f' )";
}
&ftp'send( sprintf( "SITE CHMOD %o $path", $mode ) );
#
# see what they say
$ret = &ftp'expect( $timeout,
2, 1 ); # chmod $mode $path succeeded
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
# rename a file
sub ftp'rename
{
local( $old_name, $new_name ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
if( $ftp'mapunixout ){
$old_name = eval "&$ftp'mapunixout( \$old_name, 'f' )";
}
&ftp'send( "RNFR $old_name" );
#
# see what they say
$ret = &ftp'expect( $timeout,
3, 1 ); # OK
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
# check if the "rename from" occurred ok
if( $ret ){
if( $ftp'mapunixout ){
$new_name = eval "&$ftp'mapunixout( \$new_name, 'f' )";
}
&ftp'send( "RNTO $new_name" );
#
# see what they say
$ret = &ftp'expect( $timeout,
2, 1 ); # rename $old_name to $new_name
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
}
return $ret;
}
sub ftp'quote
{
local( $cmd ) = @_;
local( $ret );
if( ! $ftp'service_open ){
return 0;
}
&ftp'send( $cmd );
$ret = &ftp'expect( $timeout,
2, 1 ); # Remote '$cmd' OK
if( $ret == 99 ){
&service_closed();
$ret = 0;
}
return $ret;
}
# ------------------------------------------------------------------------------
# These are the lower level support routines
sub ftp'expectgot
{
($ftp'resp, $ftp'fatalerror) = @_;
if( $ftp_show ){
print $ftp'showfd "$ftp'resp\n";
}
if( $ftp'keep_continuations ){
$ftp'response .= $ftp'resp;
}
else {
$ftp'response = $ftp'resp;
}
}
#
# create the list of parameters for chat'expect
#
# ftp'expect( time_out, {value, return value} );
# the last response is stored in $ftp'response
#
sub ftp'expect
{
local( $ret );
local( $time_out );
local( @expect_args );
local( $code, $pre );
$ftp'response = '';
$ftp'fatalerror = 0;
$time_out = shift( @_ );
if( $drop_on_421 ){
# Handle 421 specially - has to go first in case a pattern
# matches on a generic 4.. response
push( @expect_args, "[.|\n]*^(421 .*)\\015\\n" );
push( @expect_args, "&expectgot( \$1, 0 ); 99" );
}
# Match any obvious continuations.
push( @expect_args, "[.|\n]*^(\\d\\d\\d-.*|[^\\d].*)\\015\\n" );
push( @expect_args, "&expectgot( \$1, 0 ); 100" );
while( @_ ){
$code = shift( @_ );
$pre = '^';
$post = ' ';
if( $code =~ /^\d\d+$/ ){
$pre = "[.|\n]*^";
}
elsif( $code =~ /^\d$/ ){
$pre = "[.|\n]*^";
$post = '\d\d ';
}
push( @expect_args, "$pre(" . $code . $post . ".*)\\015\\n" );
push( @expect_args,
"&expectgot( \$1, 0 ); " . shift( @_ ) );
}
# Match any numeric response codes not explicitly looked for.
push( @expect_args, "[.|\n]*^(\\d\\d\\d .*)\\015\\n" );
push( @expect_args, "&expectgot( \$1, 0 ); 0" );
# Treat all unrecognised lines as continuations
push( @expect_args, "^(.*)\\015\\n" );
push( @expect_args, "&expectgot( \$1, 0 ); 100" );
# add patterns TIMEOUT and EOF
push( @expect_args, 'TIMEOUT' );
push( @expect_args, "&expectgot( 'timed out', 0 ); 0" );
push( @expect_args, 'EOF' );
push( @expect_args, "&expectgot( 'remote server gone away', 1 ); 99" );
# if we see a continuation line, wait for the real info
$ret = 100;
while( $ret == 100 ){
if( $ftp_show > 9 ){
&printargs( $time_out, @expect_args );
}
$ret = &chat'expect( $time_out, @expect_args );
}
return $ret;
}
#
# opens NS for io
#
sub ftp'open_data_socket
{
local( $sockaddr, $port );
local( $type, $myaddr, $a, $b, $c, $d );
local( $mysockaddr, $family, $hi, $lo );
$sockaddr = 'S n a4 x8';
($a,$b,$c,$d) = unpack( 'C4', $chat'thisaddr );
$this = $chat'thisproc;
socket( S, $pf_inet, $sock_stream, $tcp_proto ) || die "socket: $!";
bind( S, $this ) || die "bind: $!";
# get the port number
$mysockaddr = getsockname( S );
($family, $port, $myaddr) = unpack( $sockaddr, $mysockaddr );
$hi = ($port >> 8) & 0x00ff;
$lo = $port & 0x00ff;
#
# we MUST do a listen before sending the port otherwise
# the PORT may fail
#
listen( S, 5 ) || die "listen";
&ftp'send( "PORT $a,$b,$c,$d,$hi,$lo" );
return &ftp'expect( $timeout,
2, 1 ); # PORT command successful
}
sub ftp'close_data_socket
{
close( NS );
}
sub ftp'send
{
local( $send_cmd ) = @_;
if( $send_cmd =~ /\n/ ){
print $ftp'showfd "ERROR, \\n in send string for $send_cmd\n";
}
if( $ftp_show ){
local( $sc ) = $send_cmd;
if( $send_cmd =~ /^PASS/){
$sc = "PASS <somestring>";
}
print $ftp'showfd "---> $sc\n";
}
&chat'print( "$send_cmd\r\n" );
}
sub ftp'printargs
{
while( @_ ){
print $ftp'showfd shift( @_ ) . "\n";
}
}
sub ftp'filesize
{
local( $fname ) = @_;
if( ! -f $fname ){
return -1;
}
return (stat( _ ))[ 7 ];
}
# Reply codes, see RFC959:
# 1yz Positive Preliminary. Expect another reply before proceeding
# 2yz Positive Completion.
# 3yz Positive Intermediate. More information required.
# 4yz Transient Negative Completion. The user should try again.
# 5yz Permanent Negative Completion.
# x0z Syntax error
# x1z Information
# x2z Connection - control info.
# x3z Authentication and accounting.
# x4z Unspecified
# x5z File system.
# 110 Restart marker reply.
# In this case, the text is exact and not left to the
# particular implementation; it must read:
# MARK yyyy = mmmm
# Where yyyy is User-process data stream marker, and mmmm
# server's equivalent marker (note the spaces between markers
# and "=").
# 120 Service ready in nnn minutes.
# 125 Data connection already open; transfer starting.
# 150 File status okay; about to open data connection.
# 200 Command okay.
# 202 Command not implemented, superfluous at this site.
# 211 System status, or system help reply.
# 212 Directory status.
# 213 File status.
# 214 Help message.
# On how to use the server or the meaning of a particular
# non-standard command. This reply is useful only to the
# human user.
# 215 NAME system type.
# Where NAME is an official system name from the list in the
# Assigned Numbers document.
# 220 Service ready for new user.
# 221 Service closing control connection.
# Logged out if appropriate.
# 225 Data connection open; no transfer in progress.
# 226 Closing data connection.
# Requested file action successful (for example, file
# transfer or file abort).
# 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
# 230 User logged in, proceed.
# 250 Requested file action okay, completed.
# 257 "PATHNAME" created.
# 331 User name okay, need password.
# 332 Need account for login.
# 350 Requested file action pending further information.
# 421 Service not available, closing control connection.
# This may be a reply to any command if the service knows it
# must shut down.
# 425 Can't open data connection.
# 426 Connection closed; transfer aborted.
# 450 Requested file action not taken.
# File unavailable (e.g., file busy).
# 451 Requested action aborted: local error in processing.
# 452 Requested action not taken.
# Insufficient storage space in system.
# 500 Syntax error, command unrecognized.
# This may include errors such as command line too long.
# 501 Syntax error in parameters or arguments.
# 502 Command not implemented.
# 503 Bad sequence of commands.
# 504 Command not implemented for that parameter.
# 530 Not logged in.
# 532 Need account for storing files.
# 550 Requested action not taken.
# File unavailable (e.g., file not found, no access).
# 551 Requested action aborted: page type unknown.
# 552 Requested file action aborted.
# Exceeded storage allocation (for current directory or
# dataset).
# 553 Requested action not taken.
# File name not allowed.
# make this package return true
1;